package com.euc.springbootframework.dao.base.impl;

import com.euc.springbootframework.dao.base.BaseDao;
import com.euc.springbootframework.dao.pagination.BaseConditionQuery;
import com.euc.springbootframework.dao.pagination.SearchFilter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository;
import org.springframework.data.repository.NoRepositoryBean;

import java.io.Serializable;
import java.util.List;
import java.util.Objects;

/**
 * Created by WUNE on 4/23/2018.
 */
@NoRepositoryBean
public class BaseBaseDaoImpl<T, PK extends Serializable> extends SimpleMongoRepository<T, PK> implements BaseDao<T,PK> {

    @Autowired
    protected MongoTemplate mongoTemplate;

    private final MongoOperations mongoOperations;
    private final MongoEntityInformation<T, PK> entityInformation;


    public BaseBaseDaoImpl(MongoEntityInformation<T, PK> metadata, MongoOperations mongoOperations) {
        super(metadata, mongoOperations);
        this.entityInformation = metadata;
        this.mongoOperations = mongoOperations;

        targetClass = metadata.getJavaType();
    }

    protected Logger logger = LogManager.getLogger(getClass().getName());

    @SuppressWarnings({"unchecked" })
    private Class<T> targetClass;

    public Page<T> findAll(BaseConditionQuery<T> baseConditionQuery) {
        Objects.requireNonNull(baseConditionQuery);
        SearchFilter[] filters=baseConditionQuery.getSearchFilters();
        Query query=new Query();
        Criteria criteria=new Criteria();
        Criteria[] criterias=null;
        if(filters!=null){
            criterias=new Criteria[filters.length];
            int i=0;
            for(SearchFilter filter:filters){
                switch (filter.getOperator().toString()) {
                    case "EQ":
                        criterias[i]= Criteria.where(filter.getFieldName()).is(filter.getValue());
                        break;
                    case "LIKE":
                        criterias[i]= Criteria.where(filter.getFieldName()).regex(".*?"+filter.getValue()+".*", "i");
                        break;
                    case "GT":
                        criterias[i]= Criteria.where(filter.getFieldName()).gt(filter.getValue());
                        break;
                    case "GTE":
                        criterias[i]= Criteria.where(filter.getFieldName()).gte(filter.getValue());
                        break;
                    case "LT":
                        criterias[i]= Criteria.where(filter.getFieldName()).lt(filter.getValue());
                        break;
                    case "LTE":
                        criterias[i]= Criteria.where(filter.getFieldName()).lte(filter.getValue());
                        break;
                    case "IN":
                        criterias[i]= Criteria.where(filter.getFieldName()).in((Object[])filter.getValue().toString().split(","));
                    default:
                        break;
                }
                i++;
            }
            criteria.andOperator(criterias);
            query.addCriteria(criteria);
        }
        Long count=mongoTemplate.count(query, targetClass);
        Pageable pageable=baseConditionQuery.getPageable();

        Page<T> pagelist = new PageImpl<T>(mongoTemplate.find(query.with(pageable), targetClass), pageable, count);
        return pagelist;
    }


    public List<T> find(Criteria criteria){
        List<T> list=null;
        try {
            list = mongoTemplate.find(new Query(criteria), targetClass);
        } catch (Exception e) {
            logger.error(e);
        }
        return list;
    }

    public Integer findAndModify(Criteria criteria, Update update){
        T entity=null;
        try {
            entity = mongoTemplate.findAndModify(new Query(criteria), update, targetClass);
        } catch (Exception e) {
            logger.error(e);
            return 0;
        }
        return 1;
    }

    public Boolean exists(Query query){
        return mongoTemplate.exists(query, targetClass);
    }

    public T findOne(Query query){
        return mongoTemplate.findOne(query, targetClass);
    }

}
